package com.withouther.robot.rainingrobot.util.ai.tts.ali;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.setting.dialect.Props;
import com.alibaba.nls.client.AccessToken;
import com.alibaba.nls.client.protocol.NlsClient;
import com.alibaba.nls.client.protocol.OutputFormatEnum;
import com.alibaba.nls.client.protocol.SampleRateEnum;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizer;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizerListener;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizerResponse;
import com.withouther.robot.rainingrobot.util.ai.tts.model.TTSParam;
import it.sauronsoftware.jave.AudioAttributes;
import it.sauronsoftware.jave.Encoder;
import it.sauronsoftware.jave.EncodingAttributes;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@SuppressWarnings("all")
@Slf4j
public class SpeechSynthesizerUtil {
	private static long startTime;
	private String appKey;
	static NlsClient client;
	private TTSParam ttsParam;


	public SpeechSynthesizerUtil(String appKey, String accessKeyId, String accessKeySecret, TTSParam ttsParam) {
		this(appKey, accessKeyId, accessKeySecret);
		this.ttsParam = ttsParam;
	}

	public SpeechSynthesizerUtil(String appKey, String accessKeyId, String accessKeySecret) {
		this.appKey = appKey;
		//应用全局创建一个NlsClient实例，默认服务地址为阿里云线上服务地址。
		//获取token，使用时注意在accessToken.getExpireTime()过期前再次获取。
		AccessToken accessToken = new AccessToken(accessKeyId, accessKeySecret);
		try {
			accessToken.apply();
			System.out.println("get token: " + accessToken.getToken() + ", expire time: " + accessToken.getExpireTime());
			if (client == null) {
				client = new NlsClient(accessToken.getToken());
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public SpeechSynthesizerUtil(String appKey, String accessKeyId, String accessKeySecret, String url) {
		this.appKey = appKey;
		AccessToken accessToken = new AccessToken(accessKeyId, accessKeySecret);
		try {
			accessToken.apply();
			System.out.println("get token: " + accessToken.getToken() + ", expire time: " + accessToken.getExpireTime());
			if(url.isEmpty()) {
				client = new NlsClient(accessToken.getToken());
			}else {
				client = new NlsClient(url, accessToken.getToken());
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	private SpeechSynthesizerListener getSynthesizerListener() {
		SpeechSynthesizerListener listener = null;
		try {
			String filePath = this.ttsParam.getFilePath();
			listener = new SpeechSynthesizerListener() {
//				File f=new File("D:\\D\\tts_test.wav");
				File f = new File(filePath);
				FileOutputStream fout = new FileOutputStream(f);
				private boolean firstRecvBinary = true;
				//语音合成结束
				@Override
				public void onComplete(SpeechSynthesizerResponse response) {
					//调用onComplete时表示所有TTS数据已接收完成，因此为整个合成数据的延迟。该延迟可能较大，不一定满足实时场景。
					System.out.println("name: " + response.getName() +
							", status: " + response.getStatus()+
							", output file :"+f.getAbsolutePath()
					);
				}
				//语音合成的语音二进制数据
				@Override
				public void onMessage(ByteBuffer message) {
					try {
						if(firstRecvBinary) {
							//计算首包语音流的延迟，收到第一包语音流时，即可以进行语音播放，以提升响应速度（特别是实时交互场景下）。
							firstRecvBinary = false;
							long now = System.currentTimeMillis();
							log.info("tts first latency : " + (now - SpeechSynthesizerUtil.startTime) + " ms");
						}
						byte[] bytesArray = new byte[message.remaining()];
						message.get(bytesArray, 0, bytesArray.length);
						fout.write(bytesArray);
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				@Override
				public void onFail(SpeechSynthesizerResponse response){
					//task_id是调用方和服务端通信的唯一标识，当遇到问题时需要提供task_id以便排查。
					System.out.println(
							"task_id: " + response.getTaskId() +
									//状态码 20000000 表示识别成功
									", status: " + response.getStatus() +
									//错误信息
									", status_text: " + response.getStatusText());
				}
			};
		} catch (Exception e) {
			e.printStackTrace();
		}
		return listener;
	}
	public void process() {
		SpeechSynthesizer synthesizer = null;
		try {
			//创建实例，建立连接。
			synthesizer = new SpeechSynthesizer(client, getSynthesizerListener());
			synthesizer.setAppKey(appKey);
			//设置返回音频的编码格式
			synthesizer.setFormat(OutputFormatEnum.valueOf(ttsParam.getFormat()));
			//设置返回音频的采样率
			synthesizer.setSampleRate(ttsParam.getSampleRate() == 8000 ? SampleRateEnum.SAMPLE_RATE_8K : SampleRateEnum.SAMPLE_RATE_16K);
			//发音人
//			synthesizer.setVoice("Aixia");
			synthesizer.setVoice(ttsParam.getVoice());
			//语调，范围是-500~500，可选，默认是0。
			synthesizer.setPitchRate(0);
			//语速，范围是-500~500，默认是0。
			synthesizer.setSpeechRate(ttsParam.getSpeechRate());
			//设置用于语音合成的文本
			synthesizer.setText(ttsParam.getText());
			// 是否开启字幕功能（返回相应文本的时间戳），默认不开启，需要注意并非所有发音人都支持该参数。
			synthesizer.addCustomedParam("enable_subtitle", false);
			//此方法将以上参数设置序列化为JSON格式发送给服务端，并等待服务端确认。
			long start = System.currentTimeMillis();
			synthesizer.start();
			log.info("tts start latency " + (System.currentTimeMillis() - start) + " ms");
			SpeechSynthesizerUtil.startTime = System.currentTimeMillis();
			//等待语音合成结束
			synthesizer.waitForComplete();
			File file = new File(ttsParam.getFilePath());
			execute(file, "D:\\D\\69\\" + ttsParam.getText() + "1.wav");
			file.delete();
			log.info("tts stop latency " + (System.currentTimeMillis() - start) + " ms");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			//关闭连接
			if (null != synthesizer) {
				synthesizer.close();
			}
		}
	}

	/**
	 * 执行转化
	 *
	 * @param source      输入文件
	 * @param desFileName 目标文件名
	 * @return 转换之后文件
	 */
	public static File execute(File source, String desFileName) throws Exception {
		File target = new File(desFileName);
		AudioAttributes audio = new AudioAttributes();
		audio.setBitRate(new Integer(17600));
		audio.setChannels(new Integer(1));
		audio.setSamplingRate(new Integer(11025));
		EncodingAttributes attrs = new EncodingAttributes();
		attrs.setFormat("wav");
		attrs.setAudioAttributes(audio);
		Encoder encoder = new Encoder();
		encoder.encode(source, target, attrs);
		return target;
	}


	public void shutdown() {
		client.shutdown();
	}
	public static void main(String[] args) throws Exception {
		Props props = new Props("ai.properties");
		String appkey = props.getStr("ali.tts.appKey");
		String accessKeyId = props.getStr("ali.accessKey");
		String accessKeySecret = props.getStr("ali.accessSecret");
		String url = ""; //默认值：wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1
		TTSParam ttsParam = new TTSParam();
		ttsParam.setChannelId("");
		ttsParam.setFormat("WAV");
		ttsParam.setSampleRate(8000);
		ttsParam.setSpeechRate(0);
		ttsParam.setVoice("Aixia");
		ttsParam.setText("本基金存在投资冷静期，公司客服人员将在7个工作日内进行回访，请您注意接听电话，感谢您的配合！");
		ttsParam.setFilePath("D:\\D\\" + ttsParam.getText() + ".wav");
		SpeechSynthesizerUtil demo = new SpeechSynthesizerUtil(appkey, accessKeyId, accessKeySecret, ttsParam);
		demo.process();
		demo.shutdown();
	}
}
